﻿namespace X2OPC.Server.DA
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Linq;

    using X2OPC.Core;
    using X2OPC.Core.Common;
    using X2OPC.Core.DA;
    using System.Runtime.InteropServices.ComTypes;

    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IOPCShutdown))]
    public sealed class OPCDAServer : IOPCServer, IOPCCommon, IOPCBrowseServerAddressSpace, IOPCItemProperties,
        IOPCBrowse, IOPCItemIO, IOPCServerPublicGroups, IDisposable
    {

        #region  真实COM引用个数
        private int _refCount;
        private IntPtr _myUnknown;
        private delegate int OverrideAddRef(IntPtr pUnknown);
        private delegate int OverrideRelease(IntPtr pUnknown);
        private OverrideAddRef _CCWAddRef, _MyAddRef;
        private OverrideRelease _CCWRelease, _MyRelease;

        private OPCServer opcServer;

        private int dwLcid;

        private string clientName = string.Empty;

        private int NewAddRef(IntPtr pUnknown)
        {
            Interlocked.Increment(ref _refCount);
            return _CCWAddRef(pUnknown);
        }

        private int NewRelease(IntPtr pUnknown)
        {
            int ret = _CCWRelease(pUnknown);

            if (Interlocked.Decrement(ref _refCount) == 0)
            {
                ret = _CCWRelease(pUnknown);
                Dispose();
            }
            return ret;
        }

        #endregion

        private List<OPCDAGroup> opcDAGroups;

        public OPCDAServer()
        {
            #region CCW 个数 委托事件
            //  获得该对像CCW指针
            _myUnknown = Marshal.GetIUnknownForObject(this);
            //读取虚表
            IntPtr _vtable = Marshal.ReadIntPtr(_myUnknown);

            //添加 增加引用 委托
            _CCWAddRef = (OverrideAddRef)
                Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(_vtable, 1 * IntPtr.Size), typeof(OverrideAddRef));

            //添加 减少引用委托
            _CCWRelease = (OverrideRelease)
                Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(_vtable, 2 * IntPtr.Size), typeof(OverrideRelease));
            //委托绑定
            _MyRelease = new OverrideRelease(NewRelease);
            _MyAddRef = new OverrideAddRef(NewAddRef);

            //委托关联至 非托管内存
            Marshal.WriteIntPtr(_vtable, 1 * IntPtr.Size, Marshal.GetFunctionPointerForDelegate(_MyAddRef));
            Marshal.WriteIntPtr(_vtable, 2 * IntPtr.Size, Marshal.GetFunctionPointerForDelegate(_MyRelease));
            #endregion

            opcServer = OPCServer.CreateOPCServer();
            opcServer.OPCDAServers.Add(this);
            opcDAGroups = new List<OPCDAGroup>();
        }

        /// <summary>
        /// 添加OPC组
        /// 1、验证组名称是否为空
        /// 2、验证组名称是否否已存在
        /// </summary>
        /// <param name="szName">组名称</param>
        /// <param name="bActive">是否激活</param>
        /// <param name="dwRequestedUpdateRate">更新速率</param>
        /// <param name="hClientGroup">客户端组句柄</param>
        /// <param name="pTimeBias">时间</param>
        /// <param name="pPercentDeadband">死区、模拟量</param>
        /// <param name="dwLCID">语言</param>
        /// <param name="phServerGroup">服务端组句柄</param>
        /// <param name="pRevisedUpdateRate">服务端更新速率</param>
        /// <param name="riid"></param>
        /// <param name="ppUnk"></param>
        /// <returns></returns>
        public int AddGroup(string szName, bool bActive, int dwRequestedUpdateRate, int hClientGroup, IntPtr pTimeBias, IntPtr pPercentDeadband, int dwLCID, out int phServerGroup, out int pRevisedUpdateRate, ref Guid riid, out object ppUnk)
        {
            phServerGroup = 0;
            pRevisedUpdateRate = 0;
            ppUnk = null;
            if (string.IsNullOrEmpty(szName)) return HRESULTS.OPC_E_EMPTYNAME;
            if (opcDAGroups.Count(o => o.GroupName == szName) > 0) return HRESULTS.OPC_E_DUPLICATENAME;


            OPCDAGroup opcDAGroup = new OPCDAGroup(this);
            opcDAGroup.TimeBias = Marshal.ReadInt32(pTimeBias);
            float[] pdb = new float[1];
            Marshal.Copy(pPercentDeadband, pdb, 0, 1);
            opcDAGroup.LCID = dwLCID;
            opcDAGroup.Active = bActive;
            opcDAGroup.GroupName = szName;
            opcDAGroup.PercentDeadband = pdb[0];
            opcDAGroup.RevisedUpdateRate = pRevisedUpdateRate;
            opcDAGroup.ClientGroup = hClientGroup;
            phServerGroup = opcDAGroup.ServerGroup;
            ppUnk = opcDAGroup;
            opcDAGroups.Add(opcDAGroup);
            return HRESULTS.S_OK;
        }

        public int GetErrorString(int dwError, int dwLocale, out string ppString)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 根据组名称获取组对象
        /// </summary>
        /// <param name="szName">组名称</param>
        /// <param name="riid"></param>
        /// <param name="ppUnk"></param>
        /// <returns></returns>
        /// <exception cref=""></exception>
        public int GetGroupByName(string szName, ref Guid riid, out object ppUnk)
        {
            ppUnk = null;
            if (string.IsNullOrEmpty(szName)) return HRESULTS.E_INVALIDARG;
            var group = opcDAGroups.FirstOrDefault(p => p.GroupName == szName);
            if (group == null) return HRESULTS.E_FAIL;
            ppUnk = group;
            return HRESULTS.S_OK;
        }

        /// <summary>
        /// 获取服务器状态
        /// </summary>
        /// <param name="ppServerStatus"></param>
        /// <returns></returns>
        public int GetStatus(out ServerStatus ppServerStatus)
        {
            ppServerStatus = new ServerStatus();

            return HRESULTS.S_OK;
        }

        /// <summary>
        /// 移除组
        /// </summary>
        /// <param name="hServerGroup">服务端组句柄</param>
        /// <param name="bForce">是否强制删除</param>
        /// <returns></returns>
        public int RemoveGroup(int hServerGroup, bool bForce)
        {
            var group = opcDAGroups.FirstOrDefault(p => p.ServerGroup == hServerGroup);
            if (group == null) return HRESULTS.E_INVALIDARG;
            try
            {
                opcDAGroups.Remove(group);
                return HRESULTS.S_OK;
            }
            catch
            {
                return HRESULTS.E_FAIL;
            }
        }

        public int CreateGroupEnumerator(int dwScope, ref Guid riid, out object ppUnk)
        {
            ppUnk = null;
            if (Marshal.GenerateGuidForType(typeof(IEnumString)) == riid)
            {
                var groupNames = opcDAGroups.Select(p => p.GroupName).ToArray();
                EnumString gnes = new EnumString(groupNames);
                ppUnk = (IEnumString)gnes;
            }
            else
            {
                var groups = opcDAGroups.ToArray();
                ppUnk = new EnumUnknown(groups);
            }
            return HRESULTS.S_OK;
        }

        public int SetLocaleID(int dwLcid)
        {
            this.dwLcid = dwLcid;
            return HRESULTS.S_OK;
        }

        public int GetLocaleID(out int pdwLcid)
        {
            pdwLcid = dwLcid;
            return HRESULTS.S_OK;
        }

        public int QueryAvailableLocaleIDs(out int pdwCount, out IntPtr pdwLcid)
        {
            throw new NotImplementedException();
        }

        public int GetErrorString(int dwError, out string ppString)
        {
            throw new NotImplementedException();
        }

        public int SetClientName(string szName)
        {
            if (string.IsNullOrEmpty(szName)) return HRESULTS.OPC_E_EMPTYNAME;
            clientName = szName;
            return HRESULTS.S_OK;
        }

        public int QueryOrganization(out OPCNameSpaceType pNameSpaceType)
        {
            throw new NotImplementedException();
        }

        public int ChangeBrowsePosition(OPCBrowseDirection dwBrowseDirection, string szName)
        {
            throw new NotImplementedException();
        }

        public int BrowseOPCItemIDs(OPCBrowseType dwBrowseFilterType, string szFilterCriteria, short vtDataTypeFilter, OPCAccessRights dwAccessRightsFilter, out object ppUnk)
        {
            throw new NotImplementedException();
        }

        public int GetItemID(string szItemDataID, out string szItemID)
        {
            throw new NotImplementedException();
        }

        public int BrowseAccessPaths(string szItemID, out object ppUnk)
        {
            throw new NotImplementedException();
        }

        public int QueryAvailableProperties(string szItemID, out int dwCount, out IntPtr ppPropertyIDs, out IntPtr ppDescriptions, out IntPtr ppvtDataTypes)
        {
            throw new NotImplementedException();
        }

        public int GetItemProperties(string szItemID, int dwCount, int[] pdwPropertyIDs, out IntPtr ppvData, out IntPtr ppErrors)
        {
            throw new NotImplementedException();
        }

        public int LookupItemIDs(string szItemID, int dwCount, int[] pdwPropertyIDs, out IntPtr ppszNewItemIDs, out IntPtr ppErrors)
        {
            throw new NotImplementedException();
        }

        public int GetProperties(int dwItemCount, string[] pszItemIDs, bool bReturnPropertyValues, int dwPropertyCount, int[] pdwPropertyIDs, out IntPtr ppItemProperties)
        {
            throw new NotImplementedException();
        }

        public int Browse(string szItemID, ref string pszContinuationPoint, int dwMaxElementsReturned, OPCBrowseFilter dwBrowseFilter, string szElementNameFilter, string szVendorFilter, bool bReturnAllProperties, bool bReturnPropertyValues, int dwPropertyCount, int[] pdwPropertyIDs, out bool pbMoreElements, out int pdwCount, out IntPtr ppBrowseElements)
        {
            throw new NotImplementedException();
        }

        public int Read(int Count, string[] pszItemIDs, int[] pdwMaxAge, out IntPtr ppvValues, out IntPtr ppwQualities, out IntPtr ppftTimeStamps, out IntPtr ppErrors)
        {
            throw new NotImplementedException();
        }

        public int WriteVQT(int dwCount, string[] pszItemIDs, IntPtr pItemVQT, out IntPtr ppErrors)
        {
            throw new NotImplementedException();
        }

        public int GetPublicGroupByName(string szName, ref Guid riid, out object ppUnk)
        {
            throw new NotImplementedException();
        }

        public int RemovePublicGroup(int hServerGroup, bool bForce)
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }
}